:- module(repair).
:- export r_prop / 2.
:- export r_no_prop / 2.
:- export r / 2.
:- export r_conflict_prop / 3.
:- export r_conflict / 3.
:- export repair_stat / 1.
:- export call_satisfied_constraints / 0.
:- export tr_monitors / 2.
:- export tentative_ground / 2.
:- export conflict_vars / 1.
:- export conflict_constraints / 2.
:- export poss_conflict_vars / 2.
:- export conflict_constraints / 1.
:- export poss_conflict_vars / 1.
:- export r_conflict_prop / 2.
:- export r_conflict / 2.
:- export r_prop / 1.
:- export r_no_prop / 1.
:- export r / 1.
:- export tenable / 1.
:- export tent_is / 3.
:- export tent_is / 2.
:- export tent_call / 4.
:- export tent_call / 3.
:- export tent_get / 2.
:- export tent_set / 2.
:- export repair_suspensions_handler / 3.
:- export print_repair / 2.
:- export repair_unify_handler / 2.
:- export op(900, xf, r).
:- export op(900, xf, r_no_prop).
:- export op(900, xf, r_prop).
:- export op(900, xfx, r_conflict).
:- export op(900, xfx, r_conflict_prop).
:- export op(700, xfx, [tent_set, tent_get, tent_is]).
:- export struct(repair(tent, mon, ga_chg)).
:- export portray(monitor_tenable / 3, repair : tr_monitors / 2, [goal]).
:- export portray(monitor_conflict / 5, repair : tr_monitors / 2, [goal]).
:- comment(summary, "Repair library: support for local search via tentative assignments and repair constraints").
:- comment(author, "Hani El-Sakkout, Stefano Novello, Joachim Schimpf").
:- comment(copyright, "Imperial College London and ICL").
:- comment(date, "$Date: 2002/08/28 10:07:43 $").
:- comment(desc, html("The repair library provides a framework for the integration of repair-based\nsearch with the constraint consistency checking techniques of ECLiPSe. It\nallows the implementation of classical local search methods within a CLP\nenvironment.\n\nIt provides two facilities:\n\n<UL>\n\n<LI> The maintenance of tentative values for problem variables. These\n    tentative values may together form a partial or even inconsistent \n    tentative assignment.  Modifications to, or extensions of this\n    assignment may be applied until a correct solution is found.\n\n<LI> The monitoring of constraints (the so called repair constraints)\n    for being either satisfied or violated under the current tentative\n    assignment.  Search algorithms can then access the set of\n    constraints that are violated at any point in the search,\n    and perform repairs by changing the tentative assignment\n    of the problem variables.\n\n</UL><P>\nNormally, the repair library communicates with another solver (such as fd or \nria) to check for constraint violations.</P> \n")).
:- comment(index, ["local search", "tentative assignments"]).
:- comment(conflict_constraints / 1, [see_also : [conflict_constraints / 2], summary : "Obsolete: use conflict_constraints/2 instead."]).
:- comment(conflict_constraints / 2, [amode : conflict_constraints(+, -), args : ["ConflictSet" : "name or handle of a conflict set", "Constraints" : "List of conflicting constraints in ConflictSet (variable)"], summary : "Retreive the set of conflicting constraints monitored in ConflictSet.", resat : no, desc : html("<P>\nWhen a repair constraint goes into conflict (i.e. when it does not satisfy\nthe tentative assignment of its variables), it appears in a conflict set,\nonce it satisfies the tentative assignment, it disappears.\nThis primitive returns the list of all current conflict constraints\nin the given conflict set.\n\n</P><P>\nConflictSet is the conflict set name (or handle) which has\nbeen used in the corresponding constraint annotation.  For example\n\n<TT>\nconflict_constraints(cap_cstr, Conflicts)\n</TT>\n\nwould retrieve all constraints that were annotated with <TT>cap_cstr</TT>\nand are currently in conflict.\n</P>")]).
:- comment(conflict_vars / 1, [amode : conflict_vars(-), args : ["Vars" : "List of variables that are currently non-tenable"], summary : "Returns the list of variables which are currently non-tenable.", see_also : [tenable / 1, tent_set / 2], desc : html("<P>\nWhen a variable becomes untenable, it appears in the set of conflict\nvariable, when it becomes tenable, is disappears.\nThis primitive returns the list of all currently untenable variables.\nNote that all these variables must be reassigned in any solution\n(there is no other way to repair untenability).\nVariable reassignment can be achieved\nby changing the variable's tentative value with tent_set/2,\nor by instantiating the variable.\nCare should be taken whilst implementing repairs through tentative\nvalue changes since this is a non-monotonic operation: conflicting repairs\nmay lead to cycles and the computation may not terminate.  \n</P>\n")]).
:- comment(poss_conflict_vars / 2, [amode : poss_conflict_vars(+, -), args : ["ConflictSet" : "name or handle of a conflict set", "Vars" : "Variables within conflict constraints"], summary : "Returns the set of variables within the conflict constraints in ConflictSet.", desc : "The set of variables within the conflict constraints.\nThis is generally a mixture of tenable and untenable variables."]).
:- comment(r / 1, [see_also : [r_conflict / 2, r_conflict_prop / 2], summary : "Obsolete: use r_conflict/2 and r_conflict_prop/2 instead."]).
:- comment(r_conflict / 2, [amode : r_conflict(+, ?), template : "+Constraint r_conflict ?ConflictSet", args : ["Constraint" : "Constraint to be monitored for conflict (Goal)", "ConflictSet" : "Handle for the conflict set (atom or varibale)\n                argument can alternatively be ConflictSet-ConflictData"], summary : "Annotate Constraint as a repair constraint and monitor it for conflicts.", see_also : [conflict_constraints / 2, r_conflict_prop / 2], resat : no, eg : "% lib(fd) is loaded\n[eclipse 17]:  A #= B r_conflict c , B tent_set 11, A tent_set 5,  conflict_constraints(c, X).\n\nB = B{11}\nA = A{5}\nX = [A{5} #= B{11}]  % the constraint is in conflict due to tentative values\n\n[eclipse 18]: A #= B r_conflict c , B = 11, A = 5, conflict_constraints(c, X).\n\nB = 11\nA = 5\nX = [5#=11] % the constraint is in conflict due to the values of the variables\n\n A #= B r_conflict c, B tent_set 11, conflict_constraints(c, X).\n\nA = A\nB = B{11}\nX = []  % the constraint is not in conflict\n\n A::[1..10],  A #= B r_conflict c, B tent_set 11, conflict_constraints(c, X).\n\nA = A{[1..10]}\nB = B{11}\nX = [A{[1..10]} #= B{11}]\n\n[eclipse 26]:  A::[1..10],  A #= B r_conflict c, A #= B, B = 11, conflict_constraints(c, X).\n\nno (more) solution. \n% fails because A #= B is also set up as a normal constraint\n\n[eclipse 23]: A::[1..10],  A #= B r_conflict c, A #= B, B tent_set 11, conflict_constraints(c, X).\n\nA = A{fd:[1..10], repair:11}\nB = A{fd:[1..10], repair:11}\nX = [A{fd:[1..10], repair:11} #= A]\n% does not fail because the normal A #= B does not consider tenative values\n", desc : html("<P>\nRepair constraints are constraints that are monitored by the repair library \nfor conflicts caused by the tentative values of variables in the constraints. \nr_conflict/2 annotates a constraint to be a repair constraint, and performs\nthe simplest form of monitoring for violation: the repair constraint is \npassive in that it simply waits for constraint to become violated due to \nbindings to its variables or their tentative values. In such a case, the \nconstraint will show up in the ConflictSet, from where it can be\nretrieved using conflict_constraints/2.\n\n</P><P>\nNote that setting up a repair constraint does <EM>not</EM> propagate the \nconstraint as a normal constraint as well. Call the constraint again\nwithout the annotation to propagate the constraint.\n\n</P><P>\nConstraint can be any goal that works logically, it should be useable\nas a ground check, and work on any instantiation pattern. Typically,\nit will be a constraint from some solver library.\n\n</P><P>\nConflictSet can be a user-defined name (an atom) or it can be\na variable in which case the system returns a conflict set handle that can\nlater be passed to conflict_constraints/2. \n\n</P><P>\nNote that using different conflict sets for different groups of constraints\nwill often make the search algorithm easier and more efficient.\nA second allowed form of the r_conflict annotation is\nConstraint r_conflict ConflictSet-ConflictData.\nIf this is used, \bf ConflictData will appear in the conflict\nset instead of the Constraint itself.\nThis feature can be used to pass additional information to the\nsearch algorithm.\n</P>")]).
:- comment(r_conflict_prop / 2, [amode : r_conflict_prop(+, ?), template : "+Constraint r_conflict_prop ?ConflictSet", args : ["Constraint" : "Constraint to be monitored for conflict (Goal)", "ConflictSet" : "Handle for the conflict set (atom or varibale)\n                argument can alternatively be ConflictSet-ConflictData"], summary : "Annotate Constraint as a repair constraint and monitor it for conflicts. It is propagated when it goes into conflict.", see_also : [conflict_constraints / 2, r_conflict / 2], resat : no, eg : " A #= B r_conflict_prop c, A = 5, writeln(1), B = 11, write(2), conflict_constraints(c, X).\n\n1\n\nno (more) solution.\n% fails because A #= B was propagated when a conflict was detected \n", desc : html("<P>\nRepair constraints are constraints that are monitored by the repair library \nfor conflicts caused by the tentative values of variables in the constraints. \nr_conflict_prop/2 annotates a constraint to be a repair constraint, and \nas with r_conflict/2, monitors the constraint for conflicts. The difference\nis that when a violation is first detected and the Constraint enters the \nConflictSet, it is actually propagated at that point by calling the constraint.\n\n</P><P>\nNote that if you want constraint propagation from the very beginning,\nyou should simply write the constraint twice, once without and once\nwith annotation.\n\n</P><P>\nConstraint can be any goal that works logically, it should be useable\nas a ground check, and work on any instantiation pattern. Typically,\nit will be a constraint from some solver library.\n\n</P><P>\nConflictSet can be a user-defined name (an atom) or it can be\na variable in which case the system returns a conflict set handle that can\nlater be passed to conflict_constraints/2. \n\n</P><P>\nNote that using different conflict sets for different groups of constraints\nwill often make the search algorithm easier and more efficient.\nA second allowed form of the r_conflict annotation is\nConstraint r_conflict ConflictSet-ConflictData.\nIf this is used, \bf ConflictData will appear in the conflict\nset instead of the Constraint itself.\nThis feature can be used to pass additional information to the\nsearch algorithm.\n</P>")]).
:- comment(r_no_prop / 1, [see_also : [r_conflict / 2, r_conflict_prop / 2], summary : "Obsolete: use r_conflict/2 and r_conflict_prop/2 instead."]).
:- comment(r_prop / 1, [see_also : [r_conflict / 2, r_conflict_prop / 2], summary : "Obsolete: use r_conflict/2 and r_conflict_prop/2 instead."]).
:- comment(tenable / 1, [amode : tenable(?), summary : "Check if Var is tenable.", args : ["Var" : "Term"], fail_if : "Fails if Var is non-tenable.", see_also : [tent_set / 2, conflict_vars / 1], resat : no, eg : "% lib(fd) is loaded\n\n[eclipse 3]: X::1..5, X tent_set 3, tenable(X).  % suceeds\n\n[eclipse 3]: X::1..5, X tent_set 7, tenable(X).  % fails\n\n", desc : html("<P>\nSucceeds if Term is tenable. A Term is tenable if it does not contain any\nvariables with tentative values which are inconsistent with any constraints\ninvolving thevariable. Note that variables with no tentative values are\nconsidered tenable.</P>")]).
:- comment(tent_call / 3, [amode : tent_call(+, +, +), args : ["In" : "List of variables (subset of variables in Goal)", "Out" : "List of variables (subset of variables in Goal)", "Goal" : "Goal to be called"], see_also : [tent_set / 2, tent_is / 2], summary : "Eagerly call Goal whenever tentative values of variables in In changes.", desc : html("<P>\nThis is a completely general meta-predicate to support computations\nwith tentative values. Goal is a general goal, and In and Out are\nlists (or other terms) containing subsets of Goal's variables.\nA copy of Goal is called, with the In-variables replaced by their\ntentative values and the Out-variables replaced by fresh variables.\nGoal is expected to return values for the Out variables. These values\nare then used to update the tentative values of the original Out variables.\nThis process repeats whenever the tentative value of any In-variable\nchanges.\n</P>\n")]).
:- comment(tent_get / 2, [amode : tent_get(?, ?), template : "?Vars tent_get ?Values", summary : "Query the tentative values of variables in Vars.", args : ["Vars" : "Term typically with variables with tentative values", "Values" : "Term to receive tentative values of Vars."], fail_if : "Values does not unify with Vars with the tentative values filled in.", resat : no, see_also : [tent_set / 2], desc : html("<P>\nValues is a copy of the term Vars with the tentative values filled in\nplace of the variables.   If a variable has no tentative value \na variable is returned in its place.\n</P>\n<P>\nCAUTION: If a variable has no tentative value, it is not possible to\ngive it a tentative value by binding that returned variable.\ntent_set/2 must be used instead.\n</P>")]).
:- comment(tent_is / 2, [amode : tent_is(-, +), template : "-Result tent_is +Expression", args : ["Result" : "Variable", "Expression" : "Arithematic Expression"], summary : "Eagerly evaulate Expression using tentative assignments.", see_also : [(is) / 2, tent_set / 2, tent_call / 3], desc : html("<P>\nThis is similar to the normal arithmetic <TT>is/2</TT> predicate, but \nevaluates the expression based on the tentative\nassignment of its variables. The result is delivered as (an update to)\nthe tentative value of the Result variable.\nOnce initiated, tent_is will stay active and keep updating Result's\ntentative value eagerly whenever the tentative assignment of any\nvariable in Expression changes.\n</P>\n")]).
:- comment(tent_set / 2, [amode : tent_set(?, ++), template : "?Vars tent_set ++Values", args : ["Vars" : "Term with variables (non-ground term)", "Values" : "Tentative values for variables in Vars (ground term)"], summary : "Assigns tentative values for the variables in a term.", see_also : [tent_get / 2, tenable / 1], fail_if : "Vars is non-unifiable with Values", resat : no, eg : "\n% lib(fd) is loaded\n\n[eclipse 3]: X::1..5, X tent_set 3.\nX = X{fd:[1..5], repair:3} % X is tenable \n\n[eclipse 3]: X::1..5, X tent_set 7.\nX = X{fd:[1..5], repair:7} % X is non-tenable \n", desc : html("<P>\nAssociate tentative values with variables. Vars can be any non-ground term,\nand Values the corresponding ground term. The tentative values of the\nvariables are set to the ground values in Values. Typically Var is a\nvariable or a list of variables.\n\n</P></P>\nA tentative value is generally used to record preferred or previous\nassigments to this variable. It does not actually bind the variable to the\nvalue.  It can be changed through later calls to tent_set. Together with\nother tentative values and actual values for the problem variables in a\nprogram, they can form a tentative assignment which may be a partial or\ninconsistent solution to the problem. Variables with inconsistent tentative\nvalues are known as non-tenable.\n</P>\n")]).
:- comment(r_prop / 2, hidden).
:- comment(r_no_prop / 2, hidden).
:- comment(r / 2, hidden).
:- comment(r_conflict_prop / 3, hidden).
:- comment(r_conflict / 3, hidden).
:- comment(tent_is / 3, hidden).
:- comment(tent_call / 4, hidden).
